package com.jevons.geo_edit.geopackage;

import android.content.ContentValues;
import android.content.Context;

import androidx.annotation.NonNull;
import androidx.annotation.StringDef;

import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonNull;
import com.google.gson.JsonObject;
import com.jevons.geo_edit.model.GisField;
import com.jevons.geo_edit.model.GisLayer;
import com.jevons.geo_edit.model.GisLayerStyle;
import com.jevons.geo_edit.model.GisOpHistory;
import com.jevons.geo_edit.model.GisProject;
import com.jevons.geo_edit.model.GisProjectData;
import com.jevons.geo_edit.model.parse.PageListResponse;
import com.jevons.geo_edit.util.FilePathUtils;
import com.jevons.geo_lib.geojson.MapboxJsonHelper;
import com.jevons.geo_lib.utils.MapDataUtils;
import com.jevons.muffin.util.UuidUtils;
import com.jevons.muffin.utilcode.LogUtils;
import com.jevons.muffin.utilcode.StringUtils;
import com.mapbox.geojson.Feature;
import com.mapbox.geojson.Geometry;

import java.io.File;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import io.reactivex.Observable;
import io.reactivex.ObservableEmitter;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import mil.nga.geopackage.GeoPackage;
import mil.nga.geopackage.GeoPackageException;
import mil.nga.geopackage.GeoPackageFactory;
import mil.nga.geopackage.GeoPackageManager;
import mil.nga.geopackage.contents.Contents;
import mil.nga.geopackage.contents.ContentsDao;
import mil.nga.geopackage.contents.ContentsDataType;
import mil.nga.geopackage.db.GeoPackageDataType;
import mil.nga.geopackage.extension.CrsWktExtension;
import mil.nga.geopackage.features.columns.GeometryColumns;
import mil.nga.geopackage.features.columns.GeometryColumnsDao;
import mil.nga.geopackage.features.index.FeatureIndexManager;
import mil.nga.geopackage.features.index.FeatureIndexType;
import mil.nga.geopackage.features.user.FeatureColumn;
import mil.nga.geopackage.features.user.FeatureCursor;
import mil.nga.geopackage.features.user.FeatureDao;
import mil.nga.geopackage.features.user.FeatureRow;
import mil.nga.geopackage.features.user.FeatureTable;
import mil.nga.geopackage.geom.GeoPackageGeometryData;
import mil.nga.geopackage.srs.SpatialReferenceSystem;
import mil.nga.geopackage.srs.SpatialReferenceSystemDao;
import mil.nga.geopackage.user.custom.UserCustomColumn;
import mil.nga.geopackage.user.custom.UserCustomCursor;
import mil.nga.geopackage.user.custom.UserCustomDao;
import mil.nga.geopackage.user.custom.UserCustomRow;
import mil.nga.geopackage.user.custom.UserCustomTable;
import mil.nga.sf.GeometryType;
import mil.nga.sf.proj.ProjectionConstants;
import mil.nga.sf.wkt.GeometryReader;

/**
 * Copyright 2020 JxGIS
 *
 * @file WorkGeoManger
 * @auth linaisheng
 * Created on 2023/6/2.
 * Description：
 */
public class GisMapGeoManger {


    @Retention(RetentionPolicy.SOURCE)
    @StringDef({
            OP_OPEN_PROJECT,
            OP_ADD_LAYER, OP_EDIT_LAYER, OP_SORT_LAYER, OP_REMOVE_LAYER,
            OP_ADD_GEOMETRY, OP_BIANJIE_GEOMETRY, OP_QIEGE_GEOMETRY, OP_WAKONG_GEOMETRY,
            OP_HEBING_GEOMETRY, OP_REMOVE_GEOMETRY, OP_PROPERTY_GEOMETRY,
            OP_IMPORT_PATROL_ROUTE
    })
    public @interface OperationFlag {

    }

    //打开项目
    public static final String OP_OPEN_PROJECT = "openProject";
    //新增图层
    public static final String OP_ADD_LAYER = "addLayer";
    //编辑图层
    public static final String OP_EDIT_LAYER = "editLayer";
    //排序图层
    public static final String OP_SORT_LAYER = "sortLayer";
    //删除图层
    public static final String OP_REMOVE_LAYER = "removeLayer";
    //新建图形
    public static final String OP_ADD_GEOMETRY = "addGeometry";
    //调整边界
    public static final String OP_BIANJIE_GEOMETRY = "bianjieGeometry";
    //切割
    public static final String OP_QIEGE_GEOMETRY = "qiegeGeometry";
    //挖孔
    public static final String OP_WAKONG_GEOMETRY = "wakongGeometry";
    //合并
    public static final String OP_HEBING_GEOMETRY = "hebingGeometry";
    //删除
    public static final String OP_REMOVE_GEOMETRY = "removeGeometry";
    //属性
    public static final String OP_PROPERTY_GEOMETRY = "propertyGeometry";
    //导入轨迹
    public static final String OP_IMPORT_PATROL_ROUTE = "importPatrolRoute";



    public static final String GEOPACKAGE_DB_NAME = "gis_map_db";

    public static final String TABLE_GIS_PROJECT = "gis_project";
    public static final String TABLE_GIS_LAYER = "gis_layer";
    public static final String TABLE_GIS_GEOMETRY = "gis_geometry";
    public static final String TABLE_GIS_LAYER_FIELD = "gis_layer_field";
    public static final String TABLE_GIS_GEOMETRY_PROPERTY = "gis_geometry_property";

    public static final String TABLE_GIS_OP_HISTORY = "gis_op_history";
    public static final String TABLE_GIS_LAYER_HISTORY = "gis_layer_history";
    public static final String TABLE_GIS_LAYER_FIELD_HISTORY = "gis_layer_field_history";
    public static final String TABLE_GIS_GEOMETRY_HISTORY = "gis_geometry_history";
    public static final String TABLE_GIS_GEOMETRY_PROPERTY_HISTORY = "gis_geometry_property_history";

    public static final String TABLE_GIS_LAYER_STYLE = "gis_layer_style";


    private static final String COLUMN_FID = "fid";
    private static final String COLUMN_ID = "id";
    private static final String COLUMN_PROJECT_ID = "projectId";
    private static final String COLUMN_VERSION_ID = "versionId";
    private static final String COLUMN_LAYER_ID = "layerId";
    private static final String COLUMN_GEOMETRY_ID = "geometryId";
    private static final String COLUMN_FIELD_ID = "fieldId";
    private static final String COLUMN_HISTORY_ID = "historyId";
    private static final String COLUMN_GEOMETRY = "geometry";
    private static final String COLUMN_OPERATION_FLAG = "operationFlag";

    private static final String COLUMN_LAYER_TYPE = "type";
    private static final String COLUMN_LAYER_NAME = "layerName";
    private static final String COLUMN_LAYER_INDEX = "layerIndex";

    private static final String COLUMN_FIELD_NAME = "fieldName";
    private static final String COLUMN_FIELD_NOTE = "fieldNote";
    private static final String COLUMN_FIELD_VALUE = "fieldValue";


    private static final String COLUMN_FILE_ID = "fileId";

    private static final String COLUMN_CREATE_TIME = "createTime";
    private static final String COLUMN_UPDATE_TIME = "updateTime";

    private GeoPackage mGeoPackage;
    private UserCustomDao gisProjectDao;
    private UserCustomDao gisLayerDao;
    private FeatureDao gisGeometryDao;

    private UserCustomDao gisOpHistoryDao;
    private UserCustomDao gisLayerHistoryDao;
    private UserCustomDao gisLayerFieldHistoryDao;
    private FeatureDao gisGeometryHistoryDao;
    private UserCustomDao gisGeometryPropertyHistoryDao;

    private UserCustomDao gisLayerStyleDao;

    private Gson gson = new Gson();

    public GisMapGeoManger(Context context) {
        File fileDb = getSaveFile();
        if (fileDb.exists()) {
            GeoPackageManager manager = GeoPackageFactory.getManager(context);
            GeoPackage geoPackage = manager.open(GEOPACKAGE_DB_NAME);
            if (geoPackage == null) {
                throw new GeoPackageException("Failed to open database");
            }
            mGeoPackage = geoPackage;

            gisProjectDao = mGeoPackage.getUserCustomDao(TABLE_GIS_PROJECT);
            gisLayerDao = mGeoPackage.getUserCustomDao(TABLE_GIS_LAYER);
            gisGeometryDao = mGeoPackage.getFeatureDao(TABLE_GIS_GEOMETRY);

            gisOpHistoryDao = mGeoPackage.getUserCustomDao(TABLE_GIS_OP_HISTORY);
            gisLayerHistoryDao = mGeoPackage.getUserCustomDao(TABLE_GIS_LAYER_HISTORY);
            gisLayerFieldHistoryDao = mGeoPackage.getUserCustomDao(TABLE_GIS_LAYER_FIELD_HISTORY);
            gisGeometryHistoryDao = mGeoPackage.getFeatureDao(TABLE_GIS_GEOMETRY_HISTORY);
            gisGeometryPropertyHistoryDao = mGeoPackage.getUserCustomDao(TABLE_GIS_GEOMETRY_PROPERTY_HISTORY);

            gisLayerStyleDao = mGeoPackage.getUserCustomDao(TABLE_GIS_LAYER_STYLE);
            
        } else {
            throw new GeoPackageException("database file is no exist");
        }
    }


    private static File getSaveFile() {
        return new File(FilePathUtils.getDatabasePath(), GEOPACKAGE_DB_NAME + ".gpkg");
    }


    public boolean insertGisProject(GisProject project) {
        UserCustomRow row = new UserCustomRow(gisProjectDao.getTable());
        row.setValue(COLUMN_ID, project.getId());
        row.setValue("userId", project.getCreateBy());
        row.setValue("projectName", project.getName());
        row.setValue("projectNumber", project.getNumber());
        row.setValue(COLUMN_CREATE_TIME, new Date());

        long result = gisProjectDao.insert(row);
        return result > 0;
    }

    public Observable<PageListResponse<GisProject>> getGisProjectList(String userId, int pageSize, int pageNum) {
        return createObservable(new ObservableOnSubscribe<PageListResponse<GisProject>>() {
            @Override
            public void subscribe(@NonNull ObservableEmitter<PageListResponse<GisProject>> emitter) throws Exception {
                String sql = "userId = ?";

                int total = gisProjectDao.count(
                        sql, new String[]{ userId });
                List<GisProject> dataList = new ArrayList<>();

                UserCustomCursor cursor = gisProjectDao.queryForChunk(
                        sql, new String[]{ userId },
                        COLUMN_CREATE_TIME + " desc",
                        pageSize, (pageNum - 1) * pageSize);

                while (cursor.moveToNext()) {
                    UserCustomRow row = cursor.getRow();
                    GisProject item = gson.fromJson(valuesToJsonObject(row), GisProject.class);
                    dataList.add(item);
                }

                PageListResponse<GisProject> listResponse = new PageListResponse<>(dataList);
                listResponse.total = total;

                emitter.onNext(listResponse);
            }
        });
    }

    public GisProject getGisProjectDetail(String id) {
        UserCustomCursor cursor = gisProjectDao.queryForEq(COLUMN_ID, id);
        if (cursor.moveToNext()) {
            JsonObject jsonObject = valuesToJsonObject(cursor.getRow());
            GisProject gisProject = gson.fromJson(jsonObject, GisProject.class);
            return gisProject;
        }
        return null;
    }

    public Observable<Object> removeGisProject(String id) {
        return createObservable(new ObservableOnSubscribe<Object>() {
            @Override
            public void subscribe(@NonNull ObservableEmitter<Object> emitter) throws Exception {
                UserCustomCursor cursor = gisProjectDao.queryForEq(COLUMN_ID, id);
                if (cursor.moveToNext()) {
                    UserCustomRow projectRow = cursor.getRow();
                    String versionId = projectRow.getValueString(COLUMN_VERSION_ID);
                    if (StringUtils.isNotEmpty(versionId)) {
                        clearProjectVersionData(versionId);
                    }
                    //删除记录
                    boolean deleteProjectResult = gisProjectDao.delete(projectRow) > 0;
                    if (deleteProjectResult) {
                        emitter.onNext(id);
                    } else {
                        emitter.onError(new Exception("删除失败"));
                    }
                } else {
                    emitter.onError(new Exception("查询失败"));
                }
                emitter.onComplete();
            }
        });
    }


    public String existOpHistory(String projectId) {
        if (gisOpHistoryDao != null) {
            UserCustomCursor opHistoryCursor = null;
            try {
                opHistoryCursor = gisOpHistoryDao
                        .queryForChunk(COLUMN_PROJECT_ID + " = ?", new String[]{projectId},
                                COLUMN_CREATE_TIME + " desc",
                                1, 0);
                if (opHistoryCursor.moveToNext()) {
                    UserCustomRow opHistoryRow = opHistoryCursor.getRow();
                    return opHistoryRow.getValueString(COLUMN_ID);
                }
            } catch (Exception e) {} finally {
                if (opHistoryCursor != null) {
                    opHistoryCursor.close();
                }
            }
        }
        return null;
    }

    //查询上一个版本的id
    public String queryPreviousOpHistoryId(String projectId, String historyId) {
        if (gisOpHistoryDao != null) {
            String preHistoryId = null;
            UserCustomCursor historyCursor = gisOpHistoryDao.queryForEq(COLUMN_PROJECT_ID, projectId);
            while (historyCursor.moveToNext()) {
                String mId = historyCursor.getRow().getValueString(COLUMN_ID);
                if (mId.equals(historyId)) {
                    return preHistoryId;
                } else {
                    preHistoryId = mId;
                }
            }
        }
        return null;
    }

    //查询下一个版本的id
    public String queryNextOpHistoryId(String projectId, String historyId) {
        if (gisOpHistoryDao != null) {
            UserCustomCursor historyCursor = gisOpHistoryDao.queryForEq(COLUMN_PROJECT_ID, projectId);
            if (StringUtils.isNotEmpty(historyId)) {
                while (historyCursor.moveToNext()) {
                    UserCustomRow historyRow = historyCursor.getRow();
                    String mId = historyRow.getValueString(COLUMN_ID);
                    if (historyId.equals(mId)) {
                        break;
                    }
                }
            }
            //下一个
            if (historyCursor.moveToNext()) {
                String nextHistoryId = historyCursor.getRow()
                        .getValueString(COLUMN_ID);
                return nextHistoryId;
            }
        }
        return null;
    }


    public String saveHistory(String currentHistoryId, String projectId, @OperationFlag String operationFlag,
                              List<GisLayer> layers, Map<String, LinkedHashMap<String, Feature>> layerToFeatureMap) {
        if (gisOpHistoryDao == null) {
            return null;
        }

        //如果传入当前操作历史id不为空，则保存新历史时将该历史id后续的历史操作清除
        //当传入的操作历史id为空时，则是在打开项目操作状态下进行的保存，直接清空后续历史如果有的话
        List<String> followHistoryIds = new ArrayList<>();
        UserCustomCursor historyCursor = gisOpHistoryDao.queryForEq(COLUMN_PROJECT_ID, projectId);
        if (StringUtils.isNotEmpty(currentHistoryId)) {
            while (historyCursor.moveToNext()) {
                UserCustomRow historyRow = historyCursor.getRow();
                String mId = historyRow.getValueString(COLUMN_ID);
                if (currentHistoryId.equals(mId)) {
                    break;
                }
            }
        }
        //后续历史记 录
        while (historyCursor.moveToNext()) {
            String nextHistoryId = historyCursor.getRow()
                    .getValueString(COLUMN_ID);
            followHistoryIds.add(nextHistoryId);
        }


        String historyId = UuidUtils.generateId();
        UserCustomRow historyRow = new UserCustomRow(gisOpHistoryDao.getTable());
        historyRow.setValue(COLUMN_ID, historyId);
        historyRow.setValue(COLUMN_PROJECT_ID, projectId);
        historyRow.setValue(COLUMN_OPERATION_FLAG, operationFlag);
        historyRow.setValue(COLUMN_CREATE_TIME, new Date());
        if (gisOpHistoryDao.insert(historyRow) > 0) {
            //插入版本的数据内容
            for (int i = 0; i < layers.size(); i++) {
                GisLayer gisLayer = layers.get(i);
                UserCustomRow layerRow = new UserCustomRow(gisLayerHistoryDao.getTable());
                layerRow.setValue(COLUMN_ID, gisLayer.getId());
                layerRow.setValue(COLUMN_PROJECT_ID, projectId);
                layerRow.setValue(COLUMN_HISTORY_ID, historyId);
                layerRow.setValue(COLUMN_LAYER_NAME, gisLayer.getLayerName());
                layerRow.setValue(COLUMN_LAYER_TYPE, gisLayer.getType());
                layerRow.setValue(COLUMN_LAYER_INDEX, i);

                boolean layerResult = gisLayerHistoryDao.insert(layerRow) > 0;
                if (layerResult) {
                    //插入图层字段
                    Map<String, GisField> fieldMap = new HashMap<>();
                    if (gisLayer.getFieldList() != null && gisLayer.getFieldList().size() > 0) {
                        Iterator<GisField> fieldIterator = gisLayer.getFieldList().iterator();
                        while (fieldIterator.hasNext()) {
                            GisField gisField = fieldIterator.next();

                            fieldMap.put(gisField.getFieldName(), gisField);

                            UserCustomRow fieldRow = new UserCustomRow(gisLayerFieldHistoryDao.getTable());
                            fieldRow.setValue(COLUMN_ID, gisField.getId());
                            fieldRow.setValue(COLUMN_HISTORY_ID, historyId);
                            fieldRow.setValue(COLUMN_LAYER_ID, gisField.getLayerId());
                            fieldRow.setValue("sort", gisField.getSort());
                            fieldRow.setValue(COLUMN_FIELD_NAME, gisField.getFieldName());
                            fieldRow.setValue("fieldType", gisField.getFieldType());
                            fieldRow.setValue(COLUMN_FIELD_NOTE, gisField.getFieldNote());

                            fieldRow.setValue("length", gisField.getLength());
                            fieldRow.setValue("numericScale", gisField.getNumericScale());
                            fieldRow.setValue("notnull", gisField.getNotnull());
                            fieldRow.setValue("comment", gisField.getComment());
                            fieldRow.setValue("columnDefault", gisField.getColumnDefault());

                            boolean fieldResult = gisLayerFieldHistoryDao.insert(fieldRow) > 0;
                        }
                    }

                    //插入图形
                    Map<String, Feature> featureMap = layerToFeatureMap.get(gisLayer.getId());
                    if (featureMap != null && featureMap.size() > 0) {
                        Iterator<Map.Entry<String, Feature>> featureIterator = featureMap.entrySet().iterator();
                        while (featureIterator.hasNext()) {
                            Map.Entry<String, Feature> featureEntry = featureIterator.next();
                            String featureId = featureEntry.getKey();
                            Feature feature = featureEntry.getValue();
                            Geometry geometry = feature.geometry();
                            String wkt = MapDataUtils.jsonToWkt(MapboxJsonHelper.geometryToJson(geometry));
                            try {
                                GeometryReader reader = new GeometryReader(wkt);
                                GeoPackageGeometryData geomData = new GeoPackageGeometryData(reader.read());

                                FeatureRow geometryRow = gisGeometryHistoryDao.newRow();
                                geometryRow.setGeometry(geomData);
                                geometryRow.setValue(COLUMN_ID, featureId);
                                geometryRow.setValue(COLUMN_HISTORY_ID, historyId);
                                geometryRow.setValue(COLUMN_LAYER_ID, gisLayer.getId());
                                boolean geometryResult = gisGeometryHistoryDao.insert(geometryRow) > 0;

                                if (geometryResult) {
                                    //插入图形属性
                                    if (gisLayer.getFieldList() != null && gisLayer.getFieldList().size() > 0) {
                                        Iterator<GisField> fieldIterator = gisLayer.getFieldList().iterator();
                                        while (fieldIterator.hasNext()) {
                                            GisField gisField = fieldIterator.next();

                                            UserCustomRow propertyRow = new UserCustomRow(gisGeometryPropertyHistoryDao.getTable());
                                            propertyRow.setValue(COLUMN_HISTORY_ID, historyId);
                                            propertyRow.setValue(COLUMN_GEOMETRY_ID, featureId);
                                            propertyRow.setValue(COLUMN_FIELD_ID, gisField.getId());
                                            propertyRow.setValue(COLUMN_FIELD_NAME, gisField.getFieldName());
                                            if (feature.hasProperty(gisField.getId())) {
                                                String value = feature.getStringProperty(gisField.getId());
                                                propertyRow.setValue(COLUMN_FIELD_VALUE, value);
                                            }
                                            boolean propertyResult = gisGeometryPropertyHistoryDao.insert(propertyRow) > 0;
                                        }
                                    }
                                }

                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                    }



                }

            }
        }

        if (followHistoryIds.size() > 0) {
            for (String tempHistoryId : followHistoryIds) {
                deleteHistoryData(tempHistoryId);
            }
        }

        //更新样式
        if (layers != null && layers.size() > 0) {
            for (GisLayer layer : layers) {
                insertOrReplaceLayerStyle(layer.getMyLayerStyle(), true);
            }
        }
        return historyId;
    }

    public Observable<GisProjectData> loadProjectVersionData(String versionId) {
        return createObservable(new ObservableOnSubscribe<GisProjectData>() {
            @Override
            public void subscribe(@NonNull ObservableEmitter<GisProjectData> emitter) throws Exception {
                if (gisLayerDao != null) {
                    List<GisLayer> layerList = new ArrayList<>();
                    Map<String, LinkedHashMap<String, Feature>> layerToFeatureMap = new HashMap<>();
                    UserCustomCursor layerCursor = gisLayerDao.queryForEq(COLUMN_VERSION_ID, versionId);
                    while (layerCursor.moveToNext()) {
                        UserCustomRow layerRow = layerCursor.getRow();
                        GisLayer gisLayer = gson.fromJson(valuesToJsonObject(layerRow), GisLayer.class);

                        GisLayerStyle layerStyle = queryLayerStyle(gisLayer.getId(), false);
                        if (layerStyle != null) {
                            gisLayer.setMyLayerStyle(layerStyle);
                        } else {
                            gisLayer.restLayerStyle();
                        }
                        gisLayer.setLayerVisible(true);

                        layerList.add(gisLayer);
                        LinkedHashMap<String, Feature> featureMap = new LinkedHashMap<>();

                        String layerId = gisLayer.getId();
                        FeatureCursor featureCursor = gisGeometryDao.query(
                                COLUMN_LAYER_ID + " = ?", new String[]{ layerId });
                        while (featureCursor.moveToNext()) {
                            FeatureRow featureRow = featureCursor.getRow();
                            String featureId = featureRow.getValueString(COLUMN_ID);
                            String fileId = featureRow.getValueString(COLUMN_FILE_ID);

                            String wkt = featureRow.getGeometry().getWkt();

                            Feature feature = MapDataUtils.wktToFeature(wkt);
                            feature.addStringProperty("id", featureId);
                            feature.addStringProperty("fileId", fileId);
                            featureMap.put(featureId, feature);
                        }
                        layerToFeatureMap.put(layerId, featureMap);
                    }

                    GisProjectData response = new GisProjectData(layerList, layerToFeatureMap);
                    response.setVersionId(versionId);
                    emitter.onNext(response);
                } else {
                    emitter.onError(new Exception("数据查询失败"));
                }
                emitter.onComplete();
            }
        });
    }

    public Observable<GisProjectData> loadOpHistoryData(String historyId) {
        return createObservable(new ObservableOnSubscribe<GisProjectData>() {
            @Override
            public void subscribe(@NonNull ObservableEmitter<GisProjectData> emitter) throws Exception {
                if (gisLayerHistoryDao != null) {
                    List<GisLayer> layerList = new ArrayList<>();
                    Map<String, LinkedHashMap<String, Feature>> layerToFeatureMap = new HashMap<>();
                    UserCustomCursor layerCursor = gisLayerHistoryDao.query(
                            COLUMN_HISTORY_ID + " = ?", new String[]{ historyId },
                            null, null, COLUMN_LAYER_INDEX
                    );
                    try {
                        while (layerCursor.moveToNext()) {
                            UserCustomRow layerRow = layerCursor.getRow();
                            GisLayer gisLayer = gson.fromJson(valuesToJsonObject(layerRow), GisLayer.class);

                            GisLayerStyle tempLayerStyle = queryLayerStyle(gisLayer.getId(), true);
                            if (tempLayerStyle != null) {
                                gisLayer.setMyLayerStyle(tempLayerStyle);
                            } else {
                                GisLayerStyle layerStyle = queryLayerStyle(gisLayer.getId(), false);
                                if (layerStyle != null) {
                                    gisLayer.setMyLayerStyle(layerStyle);
                                } else {
                                    gisLayer.restLayerStyle();
                                }
                            }
                            gisLayer.setLayerVisible(true);

                            layerList.add(gisLayer);


                            String layerId = gisLayer.getId();

                            //查询属性
                            ArrayList<GisField> fieldList = new ArrayList<>();
                            Map<String, GisField> fieldMap = new HashMap<>();
                            UserCustomCursor fieldCursor = gisLayerFieldHistoryDao.query(
                                    COLUMN_HISTORY_ID + " = ? and "
                                            + COLUMN_LAYER_ID + " = ?",
                                    new String[]{historyId, layerId},
                                    null, null, "sort"
                            );
                            try {
                                while (fieldCursor.moveToNext()) {
                                    UserCustomRow fieldRow = fieldCursor.getRow();
                                    GisField gisField = gson.fromJson(valuesToJsonObject(fieldRow), GisField.class);
                                    fieldList.add(gisField);
                                    fieldMap.put(gisField.getId(), gisField);
                                }
                            } finally {
                                fieldCursor.close();
                            }
                            gisLayer.setFieldList(fieldList);

                            //查询图形
                            LinkedHashMap<String, Feature> featureMap = new LinkedHashMap<>();
                            FeatureCursor featureCursor = gisGeometryHistoryDao.query(
                                    COLUMN_HISTORY_ID + " = ? and " + COLUMN_LAYER_ID + " = ?",
                                    new String[]{historyId, layerId});
                            try {
                                while (featureCursor.moveToNext()) {
                                    FeatureRow featureRow = featureCursor.getRow();
                                    String featureId = featureRow.getValueString(COLUMN_ID);
                                    String fileId = featureRow.getValueString(COLUMN_FILE_ID);

                                    String wkt = featureRow.getGeometry().getWkt();

                                    Feature feature = MapDataUtils.wktToFeature(wkt);
                                    feature.addStringProperty("id", featureId);
                                    feature.addStringProperty("fileId", fileId);

                                    //查询属性值
                                    UserCustomCursor propertyCursor = gisGeometryPropertyHistoryDao.query(
                                            COLUMN_HISTORY_ID + " = ? and " + COLUMN_GEOMETRY_ID + " = ?",
                                            new String[]{historyId, featureId});
                                    try {
                                        while (propertyCursor.moveToNext()) {
                                            UserCustomRow propertyRow = propertyCursor.getRow();
                                            String fieldId = propertyRow.getValueString(COLUMN_FIELD_ID);
                                            String fieldName = propertyRow.getValueString(COLUMN_FIELD_NAME);
                                            String fieldValue = propertyRow.getValueString(COLUMN_FIELD_VALUE);
                                            if (StringUtils.isNotEmpty(fieldId)) {
                                                if (StringUtils.isNotEmpty(fieldValue)
                                                        && fieldMap.containsKey(fieldId)) {
                                                    GisField field = fieldMap.get(fieldId);
                                                    if (field.getFieldType().equals("Date") && fieldValue.contains(" ")) {
                                                        //日期型，去掉时分秒
                                                        String value = fieldValue.split(" ")[0];
                                                        feature.addStringProperty(
                                                                fieldId, value);
                                                    } else {
                                                        feature.addStringProperty(
                                                                fieldId, fieldValue);
                                                    }
                                                } else {
                                                    feature.addStringProperty(
                                                            fieldId, fieldValue);
                                                }
                                            }
                                        }
                                    } finally {
                                        propertyCursor.close();
                                    }


                                    featureMap.put(featureId, feature);
                                }
                            } finally {
                                featureCursor.close();
                            }


                            layerToFeatureMap.put(layerId, featureMap);
                        }
                    } finally {
                        layerCursor.close();
                    }

                    GisProjectData response = new GisProjectData(layerList, layerToFeatureMap);
                    response.setHistoryId(historyId);
                    emitter.onNext(response);
                } else {
                    emitter.onError(new Exception("数据查询失败"));
                }
                emitter.onComplete();
            }
        });
    }

    public boolean submitSaveVersion(String projectId, String historyId) {
        return submitSaveVersion(projectId, historyId, true);
    }

    //将历史版本保存
    public boolean submitSaveVersion(String projectId, String historyId, boolean clearHistory) {
        if (gisLayerDao != null && gisGeometryDao != null
                && gisOpHistoryDao != null
                && gisLayerHistoryDao != null && gisGeometryHistoryDao != null) {

            UserCustomCursor projectCursor = gisProjectDao.queryForEq(COLUMN_ID, projectId);
            if (projectCursor.moveToNext()) {

                UserCustomRow projectRow = projectCursor.getRow();
                //清除旧数据，
                String oldVersionId = projectRow.getValueString(COLUMN_VERSION_ID);
                if (StringUtils.isNotEmpty(oldVersionId)) {
                    clearProjectVersionData(oldVersionId);
                }

                //将历史记录插入新数据
                String versionId = UuidUtils.generateId();
                UserCustomCursor layerHistoryCursor = gisLayerHistoryDao.queryForEq(COLUMN_HISTORY_ID, historyId);
                while (layerHistoryCursor.moveToNext()) {
                    UserCustomRow layerHistoryRow = layerHistoryCursor.getRow();
                    String layerId = layerHistoryRow.getValueString(COLUMN_ID);

                    //新增
                    UserCustomRow layerRow = gisLayerDao.newRow();
                    layerRow.setValue(COLUMN_ID, layerId);
                    layerRow.setValue(COLUMN_PROJECT_ID, projectId);
                    layerRow.setValue(COLUMN_LAYER_TYPE, layerHistoryRow.getValue(COLUMN_LAYER_TYPE));
                    layerRow.setValue(COLUMN_LAYER_NAME, layerHistoryRow.getValue(COLUMN_LAYER_NAME));
                    layerRow.setValue(COLUMN_VERSION_ID, versionId);

                    boolean layerResult = gisLayerDao.insert(layerRow) > 0;
                    LogUtils.printError("insert layerResult:" + layerResult);

                    //查询改图层有没有图形
                    FeatureCursor geometryHistoryCursor = gisGeometryHistoryDao
                            .query(COLUMN_HISTORY_ID + " = ? and " + COLUMN_LAYER_ID + " = ?", new String[]{ historyId, layerId });
                    while (geometryHistoryCursor.moveToNext()) {
                        FeatureRow geometryHistoryRow = geometryHistoryCursor.getRow();
                        //新增
                        FeatureRow geometryRow = gisGeometryDao.newRow();
                        geometryRow.setGeometry(geometryHistoryRow.getGeometry());
                        geometryRow.setValue(COLUMN_ID, geometryHistoryRow.getValue(COLUMN_ID));
                        geometryRow.setValue(COLUMN_LAYER_ID, layerId);
                        geometryRow.setValue(COLUMN_FILE_ID, geometryRow.getValue(COLUMN_FILE_ID));
                        boolean geometryResult = gisGeometryDao.insert(geometryRow) > 0;
                        LogUtils.printError("insert geometryResult:" + geometryResult);
                    }

                    tempLayerSaveToProjectStyle(projectId);
                }

                //清除历史记录
                if (clearHistory) {
                    clearOpHistoryData(projectId);
                }


                //更新项目版本号
                projectRow.setValue(COLUMN_VERSION_ID, versionId);
                projectRow.setValue(COLUMN_UPDATE_TIME, new Date());
                boolean projectResult = gisProjectDao.update(projectRow) > 0;
                LogUtils.printError("update projectResult:" + projectResult);
                return projectResult;
            }
        }
        return false;
    }

    private void clearProjectVersionData(String versionId) {
        UserCustomCursor layerCursor = gisLayerDao.query(
                COLUMN_VERSION_ID + " = ?",
                new String[]{ versionId });
        while (layerCursor.moveToNext()) {
            UserCustomRow layerRow = layerCursor.getRow();
            String layerId = layerRow.getValueString(COLUMN_ID);
            //查询并删除图形
            gisGeometryDao.delete(COLUMN_LAYER_ID + " = ?", new String[]{ layerId });

            boolean deleteLayerResult = gisLayerDao.delete(layerRow) > 0;
            LogUtils.printError("deleteLayerResult:" + deleteLayerResult);
        }
    }

    public List<GisOpHistory> queryGisOpHistoryList(String projectId) {
        if (gisOpHistoryDao != null) {
            List<GisOpHistory> dataList = new ArrayList<>();
            UserCustomCursor historyCursor = gisOpHistoryDao.queryForEq(COLUMN_PROJECT_ID, projectId);
            while (historyCursor.moveToNext()) {
                GisOpHistory item = gson.fromJson(valuesToJsonObject(historyCursor.getRow()), GisOpHistory.class);
                dataList.add(item);
            }
            return dataList;
        }
        return new ArrayList<>();
    }

    public void clearOpHistoryData(String projectId) {
        UserCustomCursor historyCursor = gisOpHistoryDao.queryForEq(COLUMN_PROJECT_ID, projectId);
        String tempHistoryId = null;
        try {
            while (historyCursor.moveToNext()) {
                UserCustomRow historyRow = historyCursor.getRow();
                tempHistoryId = historyRow.getValueString(COLUMN_ID);
                if (StringUtils.isNotEmpty(tempHistoryId)) {
                    deleteHistoryData(tempHistoryId);
                }
            }
        } finally {
            historyCursor.close();
        }
    }

    private void deleteHistoryData(String historyId) {
        gisOpHistoryDao.delete(COLUMN_ID + " = ?",
                new String[]{ historyId });
        gisLayerHistoryDao.delete(COLUMN_HISTORY_ID + " = ?",
                new String[]{ historyId });
        gisLayerFieldHistoryDao.delete(COLUMN_HISTORY_ID + " = ?",
                new String[]{ historyId });
        gisGeometryHistoryDao.delete(COLUMN_HISTORY_ID + " = ?",
                new String[]{ historyId });
    }

    public void insertOrReplaceLayerStyle(GisLayerStyle layerStyle, boolean isTemp) {
        if (gisLayerStyleDao != null) {
            long timeMillis = System.currentTimeMillis();
            UserCustomCursor styleCursor = gisLayerStyleDao.query(
                    COLUMN_LAYER_ID + " = ? and isTemp = ?",
                    new String[]{ layerStyle.getLayerId(), isTemp ? "1" : "0" });
            if (styleCursor.moveToNext()) {
                //更新
                UserCustomRow styleRow = styleCursor.getRow();
                styleRow = getLocalRowData(styleRow,
                        (JsonObject) gson.toJsonTree(layerStyle));
                styleRow.setValue(COLUMN_UPDATE_TIME, new Date(timeMillis));
                styleRow.setValue("isTemp", isTemp ? 1 : 0);
                gisLayerStyleDao.update(styleRow);
            } else {
                //新增
                UserCustomRow styleRow = gisLayerStyleDao.newRow();
                styleRow = getLocalRowData(styleRow,
                        (JsonObject) gson.toJsonTree(layerStyle));
                styleRow.setValue(COLUMN_UPDATE_TIME, new Date(timeMillis));
                styleRow.setValue("isTemp", isTemp ? 1 : 0);
                gisLayerStyleDao.insert(styleRow);
            }
            layerStyle.setUpdateTime(timeMillis);
        }
    }

    public GisLayerStyle queryLayerStyle(String layerId, boolean isTemp) {
        if (gisLayerStyleDao != null) {
            UserCustomCursor styleCursor = gisLayerStyleDao.query(
                    COLUMN_LAYER_ID + " = ? and isTemp = " + (isTemp ? 1 : 0),
                    new String[]{ layerId });
            if (styleCursor.moveToNext()) {
                UserCustomRow styleRow = styleCursor.getRow();
                GisLayerStyle gisLayerStyle = gson.fromJson(
                        valuesToJsonObject(styleRow), GisLayerStyle.class);
                return gisLayerStyle;
            }
        }
        return null;
    }

    public boolean existLayerStyle(String projectId, boolean isTemp) {
        if (gisLayerStyleDao != null) {
            UserCustomCursor styleCursor = gisLayerStyleDao.query(
                    COLUMN_PROJECT_ID + " = ? and isTemp = " + (isTemp ? 1 : 0),
                    new String[]{ projectId });
            if (styleCursor.moveToNext()) {
                UserCustomRow styleRow = styleCursor.getRow();
                GisLayerStyle gisLayerStyle = gson.fromJson(
                        valuesToJsonObject(styleRow), GisLayerStyle.class);
                return true;
            }
        }
        return false;
    }

    public void tempLayerSaveToProjectStyle(String projectId) {
        if (gisLayerStyleDao != null) {
            //删除非临时样式数据，将临时样式数据进行保存为正式数据
            boolean resultDelete = gisLayerStyleDao.delete(
                    COLUMN_PROJECT_ID + " = ? and isTemp = 0",
                    new String[]{ projectId }) > 0;

            ContentValues contentValues = new ContentValues();
            contentValues.put("isTemp", 0);
            boolean resultUpdate = gisLayerStyleDao.update(contentValues,
                    COLUMN_PROJECT_ID + " = ? and isTemp = 1",
                    new String[]{ projectId }) > 0;
            LogUtils.printError("resultDelete:" + resultDelete + ",resultUpdate:" + resultDelete);
        }
    }


    public void closeDb() {
        mGeoPackage.close();
        mGeoPackage = null;

        gisProjectDao = null;
        gisLayerDao = null;
        gisGeometryDao = null;

    }

    private <T> Observable<T> createObservable(ObservableOnSubscribe<T> subscribe) {
        return Observable.create(subscribe)
                .subscribeOn(Schedulers.io())
                .unsubscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread());
    }


    protected JsonObject valuesToJsonObject(UserCustomRow row) {
        JsonObject jsonObject = new JsonObject();

        ContentValues values = row.toContentValues();
        Iterator<String> iterator = values.keySet().iterator();
        while (iterator.hasNext()) {
            String key = iterator.next();
            GeoPackageDataType dataType = row.getColumn(key).getDataType();
            if (dataType == GeoPackageDataType.BOOLEAN) {
                jsonObject.addProperty(key, values.getAsBoolean(key));
            } else if (dataType == GeoPackageDataType.TINYINT) {
                jsonObject.addProperty(key, values.getAsByte(key));
            } else if (dataType == GeoPackageDataType.SMALLINT) {
                jsonObject.addProperty(key, values.getAsShort(key));
            } else if (dataType == GeoPackageDataType.MEDIUMINT) {
                jsonObject.addProperty(key, values.getAsInteger(key));
            } else if (dataType == GeoPackageDataType.INT) {
                jsonObject.addProperty(key, values.getAsLong(key));
            } else if (dataType == GeoPackageDataType.INTEGER) {
                jsonObject.addProperty(key, values.getAsLong(key));
            } else if (dataType == GeoPackageDataType.FLOAT) {
                jsonObject.addProperty(key, values.getAsFloat(key));
            } else if (dataType == GeoPackageDataType.DOUBLE) {
                jsonObject.addProperty(key, values.getAsDouble(key));
            } else if (dataType == GeoPackageDataType.REAL) {
                jsonObject.addProperty(key, values.getAsDouble(key));
            } else if (dataType == GeoPackageDataType.TEXT) {
                jsonObject.addProperty(key, values.getAsString(key));
            } else if (dataType == GeoPackageDataType.BLOB) {
//                jsonObject.addProperty(key, values.getAsByteArray(key));
            } else if (dataType == GeoPackageDataType.DATE) {
                Object dateObject = row.getValue(key);
                if (dateObject instanceof Date) {
                    Date date = (Date) dateObject;
                    jsonObject.addProperty(key, date.getTime());
                } else {
                    String value = null;
                    jsonObject.addProperty(key, value);
                }
            } else if (dataType == GeoPackageDataType.DATETIME) {
                Object dateObject = row.getValue(key);
                if (dateObject instanceof Date) {
                    Date date = (Date) dateObject;
                    jsonObject.addProperty(key, date.getTime());
                } else {
                    String value = null;
                    jsonObject.addProperty(key, value);
                }
            }
        }

        return jsonObject;
    }

    protected UserCustomRow getLocalRowData(UserCustomRow customRow, JsonObject property) {
        Iterator<Map.Entry<String, JsonElement>> iterator = property.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, JsonElement> entry = iterator.next();
            if (!customRow.hasColumn(entry.getKey())) {
                continue;
            }
            UserCustomColumn column = customRow.getColumn(entry.getKey());

            JsonElement element = entry.getValue();
            if (element == null) {
                customRow.setValue(entry.getKey(), null);
            } else if (element instanceof JsonNull) {
                customRow.setValue(entry.getKey(), null);
            } else {
                Object object = getUserColumnValue(column, entry.getValue().getAsString());
                customRow.setValue(entry.getKey(), object);
            }
        }
        return customRow;
    }

    protected Object getUserColumnValue(UserCustomColumn column, String value) {
        GeoPackageDataType dataType = column.getDataType();
        if (StringUtils.isEmpty(value)) {
            return null;
        }

        if (dataType == GeoPackageDataType.TEXT) {
            return value;
        } else if (dataType == GeoPackageDataType.BOOLEAN) {
            return Boolean.parseBoolean(value);
        } else if (dataType == GeoPackageDataType.TINYINT) {
            return Byte.parseByte(value);
        } else if (dataType == GeoPackageDataType.SMALLINT) {
            return Short.parseShort(value);
        } else if (dataType == GeoPackageDataType.MEDIUMINT) {
            return Integer.parseInt(value);
        } else if (dataType == GeoPackageDataType.INT || dataType == GeoPackageDataType.INTEGER) {
            return Long.parseLong(value);
        } else if (dataType == GeoPackageDataType.FLOAT) {
            return Float.parseFloat(value);
        } else if (dataType == GeoPackageDataType.REAL || dataType == GeoPackageDataType.DOUBLE) {
            return Double.parseDouble(value);
        } else if (dataType == GeoPackageDataType.DATE || dataType == GeoPackageDataType.DATETIME) {
            Long dateTime = Long.parseLong(value);
            return new Date(dateTime);
        }
        return null;
    }


    public static void checkExistDbFileAndCreate(Context context) {
        File fileDb = getSaveFile();
        if (!fileDb.exists()) {
            GeoPackageManager manager = GeoPackageFactory.getManager(context);
            manager.delete(GEOPACKAGE_DB_NAME);
            manager.createFile(GEOPACKAGE_DB_NAME, fileDb);

            GeoPackage geoPackage = manager.open(GEOPACKAGE_DB_NAME);
            if (geoPackage == null) {
                throw new GeoPackageException("Failed to open database");
            } else {
                try {
                    //新增坐标系
                    createCrsWktExtension(geoPackage);

                    //创建表
                    createTable(geoPackage);

                    //创建索引
                    createGeometryIndexExtension(context, geoPackage);

                    geoPackage.close();
                } catch (Exception e){
                    e.printStackTrace();
                }
                LogUtils.printError(GEOPACKAGE_DB_NAME + ": done");
            }
        }
    }

    //新增4490坐标系
    private static void createCrsWktExtension(GeoPackage geoPackage)
            throws SQLException {

        CrsWktExtension wktExtension = new CrsWktExtension(geoPackage);
        wktExtension.getOrCreate();

        SpatialReferenceSystemDao srsDao = geoPackage
                .getSpatialReferenceSystemDao();

        SpatialReferenceSystem srs4490 = new SpatialReferenceSystem();
        srs4490.setSrsName("epsg:4490");
        srs4490.setSrsId(4490);
        srs4490.setOrganization(ProjectionConstants.AUTHORITY_EPSG);
        srs4490.setOrganizationCoordsysId(srs4490.getSrsId());
        srs4490.setDefinition("GEOGCS[\"China Geodetic Coordinate System 2000\", \n" +
                "  DATUM[\"China 2000\", \n" +
                "    SPHEROID[\"CGCS2000\", 6378137.0, 298.257222101, AUTHORITY[\"EPSG\",\"1024\"]], \n" +
                "    AUTHORITY[\"EPSG\",\"1043\"]], \n" +
                "  PRIMEM[\"Greenwich\", 0.0, AUTHORITY[\"EPSG\",\"8901\"]], \n" +
                "  UNIT[\"degree\", 0.017453292519943295], \n" +
                "  AXIS[\"Geodetic longitude\", EAST], \n" +
                "  AXIS[\"Geodetic latitude\", NORTH], \n" +
                "  AUTHORITY[\"EPSG\",\"4490\"]]");
        srs4490.setDescription("epsg:4490");
        srsDao.create(srs4490);

    }

    private static void createTable(GeoPackage geoPackage) throws SQLException {
        createGisProjectTable(geoPackage);
        createGisLayer(geoPackage);
        createGisLayerFieldTable(geoPackage);
        createGisGeometryTable(geoPackage);
        createGisGeometryPropertyTable(geoPackage);

        createGisOpHistoryTable(geoPackage);
        createGisLayerHistoryTable(geoPackage);
        createGisLayerFieldHistoryTable(geoPackage);
        createGisGeometryHistoryTable(geoPackage);
        createGisGeometryPropertyHistoryTable(geoPackage);

        createGisLayerStyleTable(geoPackage);
    }

    private static void createGisProjectTable(GeoPackage geoPackage) {
        List<UserCustomColumn> columns = new ArrayList<UserCustomColumn>();

        columns.add(UserCustomColumn.createPrimaryKeyColumn(COLUMN_FID));
        columns.add(UserCustomColumn.createColumn(COLUMN_ID,
                GeoPackageDataType.TEXT, true));
        columns.add(UserCustomColumn.createColumn("userId",
                GeoPackageDataType.TEXT));
        columns.add(UserCustomColumn.createColumn("projectName",
                GeoPackageDataType.TEXT));
        columns.add(UserCustomColumn.createColumn("projectNumber",
                GeoPackageDataType.TEXT));
        columns.add(UserCustomColumn.createColumn(COLUMN_VERSION_ID,
                GeoPackageDataType.TEXT));
        columns.add(UserCustomColumn.createColumn(COLUMN_CREATE_TIME,
                GeoPackageDataType.DATETIME));
        columns.add(UserCustomColumn.createColumn(COLUMN_UPDATE_TIME,
                GeoPackageDataType.DATETIME));

        UserCustomTable gisProjectTable = new UserCustomTable(TABLE_GIS_PROJECT, columns);
        geoPackage.createUserTable(gisProjectTable);

    }

    private static void createGisLayer(GeoPackage geoPackage) {
        List<UserCustomColumn> columns = new ArrayList<>();

        columns.add(UserCustomColumn.createPrimaryKeyColumn(COLUMN_FID));
        columns.add(UserCustomColumn.createColumn(COLUMN_ID,
                GeoPackageDataType.TEXT, true));
        columns.add(UserCustomColumn.createColumn(COLUMN_PROJECT_ID,
                GeoPackageDataType.TEXT, true));
        columns.add(UserCustomColumn.createColumn(COLUMN_VERSION_ID,
                GeoPackageDataType.TEXT));
        columns.add(UserCustomColumn.createColumn(COLUMN_LAYER_TYPE,
                GeoPackageDataType.INTEGER, true, 1));
        columns.add(UserCustomColumn.createColumn(COLUMN_LAYER_NAME,
                GeoPackageDataType.TEXT));
        columns.add(UserCustomColumn.createColumn(COLUMN_LAYER_INDEX,
                GeoPackageDataType.INTEGER));


        UserCustomTable gisLayerTable = new UserCustomTable(TABLE_GIS_LAYER, columns);
        geoPackage.createUserTable(gisLayerTable);

    }

    private static void createGisGeometryTable(GeoPackage geoPackage) throws SQLException {
        SpatialReferenceSystemDao srsDao = geoPackage
                .getSpatialReferenceSystemDao();

        SpatialReferenceSystem srs = srsDao.getOrCreateCode(
                ProjectionConstants.AUTHORITY_EPSG, (long) 4490);

        geoPackage.createGeometryColumnsTable();

        ContentsDao contentsDao = geoPackage.getContentsDao();
        Contents contents = new Contents();
        contents.setTableName(TABLE_GIS_GEOMETRY);
        contents.setDataType(ContentsDataType.FEATURES);
        contents.setIdentifier(TABLE_GIS_GEOMETRY);
        contents.setDescription(TABLE_GIS_GEOMETRY + " table");
        contents.setMinX(0d);
        contents.setMinY(0d);
        contents.setMaxX(180d);
        contents.setMaxY(90d);
        contents.setSrs(srs);

        List<FeatureColumn> columns = new ArrayList<FeatureColumn>();

        columns.add(FeatureColumn.createPrimaryKeyColumn(COLUMN_FID));
        columns.add(FeatureColumn.createColumn(COLUMN_ID,
                GeoPackageDataType.TEXT, true));
        columns.add(FeatureColumn.createColumn(COLUMN_LAYER_ID,
                GeoPackageDataType.TEXT, true));
        columns.add(FeatureColumn.createColumn(COLUMN_FILE_ID,
                GeoPackageDataType.TEXT, false, ""));
        columns.add(FeatureColumn.createGeometryColumn(COLUMN_GEOMETRY, GeometryType.GEOMETRY));

        FeatureTable table = new FeatureTable(TABLE_GIS_GEOMETRY, columns);
        geoPackage.createFeatureTable(table);

        contentsDao.create(contents);

        GeometryColumnsDao geometryColumnsDao = geoPackage
                .getGeometryColumnsDao();

        GeometryColumns geometryColumns = new GeometryColumns();
        geometryColumns.setContents(contents);
        geometryColumns.setColumnName(COLUMN_GEOMETRY);
        geometryColumns.setGeometryType(GeometryType.GEOMETRY);
        geometryColumns.setSrs(srs);
        geometryColumns.setZ((byte) 0);
        geometryColumns.setM((byte) 0);
        geometryColumnsDao.create(geometryColumns);
    }

    private static void createGisLayerFieldTable(GeoPackage geoPackage) {
        List<UserCustomColumn> columns = new ArrayList<UserCustomColumn>();

        columns.add(UserCustomColumn.createPrimaryKeyColumn(COLUMN_FID));
        columns.add(UserCustomColumn.createColumn(COLUMN_ID,
                GeoPackageDataType.TEXT, true));
        columns.add(UserCustomColumn.createColumn(COLUMN_LAYER_ID,
                GeoPackageDataType.TEXT));
        columns.add(UserCustomColumn.createColumn("sort",
                GeoPackageDataType.INTEGER, true, 1));

        columns.add(UserCustomColumn.createColumn(COLUMN_FIELD_NAME,
                GeoPackageDataType.TEXT));
        columns.add(UserCustomColumn.createColumn("fieldType",
                GeoPackageDataType.TEXT));
        columns.add(UserCustomColumn.createColumn(COLUMN_FIELD_NOTE,
                GeoPackageDataType.TEXT));

        columns.add(UserCustomColumn.createColumn("length",
                GeoPackageDataType.INTEGER));
        columns.add(UserCustomColumn.createColumn("numericScale",
                GeoPackageDataType.INTEGER));
        columns.add(UserCustomColumn.createColumn("notnull",
                GeoPackageDataType.TEXT));
        columns.add(UserCustomColumn.createColumn("comment",
                GeoPackageDataType.TEXT));
        columns.add(UserCustomColumn.createColumn("columnDefault",
                GeoPackageDataType.TEXT));

        UserCustomTable gisLayerFieldTable = new UserCustomTable(TABLE_GIS_LAYER_FIELD, columns);
        geoPackage.createUserTable(gisLayerFieldTable);
    }

    private static void createGisGeometryPropertyTable(GeoPackage geoPackage) {
        List<UserCustomColumn> columns = new ArrayList<UserCustomColumn>();

        columns.add(UserCustomColumn.createPrimaryKeyColumn(COLUMN_FID));
        columns.add(UserCustomColumn.createColumn(COLUMN_ID,
                GeoPackageDataType.TEXT, true));
        columns.add(UserCustomColumn.createColumn(COLUMN_GEOMETRY_ID,
                GeoPackageDataType.TEXT));
        columns.add(UserCustomColumn.createColumn(COLUMN_FIELD_ID,
                GeoPackageDataType.TEXT));
        columns.add(UserCustomColumn.createColumn(COLUMN_FIELD_NAME,
                GeoPackageDataType.TEXT));
        columns.add(UserCustomColumn.createColumn(COLUMN_FIELD_VALUE,
                GeoPackageDataType.TEXT));

        UserCustomTable gisGeomtryPropertyTable = new UserCustomTable(TABLE_GIS_GEOMETRY_PROPERTY, columns);
        geoPackage.createUserTable(gisGeomtryPropertyTable);
    }


    private static void createGisOpHistoryTable(GeoPackage geoPackage) {
        List<UserCustomColumn> columns = new ArrayList<UserCustomColumn>();

        columns.add(UserCustomColumn.createPrimaryKeyColumn(COLUMN_FID));
        columns.add(UserCustomColumn.createColumn(COLUMN_ID,
                GeoPackageDataType.TEXT, true));
        columns.add(UserCustomColumn.createColumn(COLUMN_PROJECT_ID,
                GeoPackageDataType.TEXT, true));
        columns.add(UserCustomColumn.createColumn(COLUMN_OPERATION_FLAG,
                GeoPackageDataType.TEXT));
        columns.add(UserCustomColumn.createColumn(COLUMN_CREATE_TIME,
                GeoPackageDataType.DATETIME));

        UserCustomTable gisVersionTable = new UserCustomTable(TABLE_GIS_OP_HISTORY, columns);
        geoPackage.createUserTable(gisVersionTable);
    }

    private static void createGisLayerHistoryTable(GeoPackage geoPackage) {
        List<UserCustomColumn> columns = new ArrayList<UserCustomColumn>();

        columns.add(UserCustomColumn.createPrimaryKeyColumn(COLUMN_FID));
        columns.add(UserCustomColumn.createColumn(COLUMN_ID,
                GeoPackageDataType.TEXT, true));
        columns.add(UserCustomColumn.createColumn(COLUMN_PROJECT_ID,
                GeoPackageDataType.TEXT));
        columns.add(UserCustomColumn.createColumn(COLUMN_HISTORY_ID,
                GeoPackageDataType.TEXT));
        columns.add(UserCustomColumn.createColumn(COLUMN_LAYER_TYPE,
                GeoPackageDataType.INTEGER, true, 1));
        columns.add(UserCustomColumn.createColumn(COLUMN_LAYER_NAME,
                GeoPackageDataType.TEXT));
        columns.add(UserCustomColumn.createColumn(COLUMN_LAYER_INDEX,
                GeoPackageDataType.INTEGER));


        UserCustomTable gisVersionTable = new UserCustomTable(TABLE_GIS_LAYER_HISTORY, columns);
        geoPackage.createUserTable(gisVersionTable);
    }


    private static void createGisLayerFieldHistoryTable(GeoPackage geoPackage) {
        List<UserCustomColumn> columns = new ArrayList<UserCustomColumn>();

        columns.add(UserCustomColumn.createPrimaryKeyColumn(COLUMN_FID));
        columns.add(UserCustomColumn.createColumn(COLUMN_ID,
                GeoPackageDataType.TEXT, true));
        columns.add(UserCustomColumn.createColumn(COLUMN_HISTORY_ID,
                GeoPackageDataType.TEXT, true));
        columns.add(UserCustomColumn.createColumn(COLUMN_LAYER_ID,
                GeoPackageDataType.TEXT, true));

        columns.add(UserCustomColumn.createColumn("sort",
                GeoPackageDataType.INTEGER, true, 0));
        columns.add(UserCustomColumn.createColumn(COLUMN_FIELD_NAME,
                GeoPackageDataType.TEXT));
        columns.add(UserCustomColumn.createColumn("fieldType",
                GeoPackageDataType.TEXT));
        columns.add(UserCustomColumn.createColumn(COLUMN_FIELD_NOTE,
                GeoPackageDataType.TEXT));

        columns.add(UserCustomColumn.createColumn("length",
                GeoPackageDataType.INTEGER));
        columns.add(UserCustomColumn.createColumn("numericScale",
                GeoPackageDataType.INTEGER));
        columns.add(UserCustomColumn.createColumn("notnull",
                GeoPackageDataType.TEXT));
        columns.add(UserCustomColumn.createColumn("comment",
                GeoPackageDataType.TEXT));
        columns.add(UserCustomColumn.createColumn("columnDefault",
                GeoPackageDataType.TEXT));

        UserCustomTable gisLayerFieldTable = new UserCustomTable(TABLE_GIS_LAYER_FIELD_HISTORY, columns);
        geoPackage.createUserTable(gisLayerFieldTable);
    }

    private static void createGisGeometryHistoryTable(GeoPackage geoPackage) throws SQLException {
        SpatialReferenceSystemDao srsDao = geoPackage
                .getSpatialReferenceSystemDao();

        SpatialReferenceSystem srs = srsDao.getOrCreateCode(
                ProjectionConstants.AUTHORITY_EPSG, (long) 4490);

        geoPackage.createGeometryColumnsTable();

        ContentsDao contentsDao = geoPackage.getContentsDao();
        Contents contents = new Contents();
        contents.setTableName(TABLE_GIS_GEOMETRY_HISTORY);
        contents.setDataType(ContentsDataType.FEATURES);
        contents.setIdentifier(TABLE_GIS_GEOMETRY_HISTORY);
        contents.setDescription(TABLE_GIS_GEOMETRY_HISTORY + " table");
        contents.setMinX(0d);
        contents.setMinY(0d);
        contents.setMaxX(180d);
        contents.setMaxY(90d);
        contents.setSrs(srs);

        List<FeatureColumn> columns = new ArrayList<FeatureColumn>();

        columns.add(FeatureColumn.createPrimaryKeyColumn(COLUMN_FID));
        columns.add(FeatureColumn.createColumn(COLUMN_ID,
                GeoPackageDataType.TEXT, true));
        columns.add(FeatureColumn.createColumn(COLUMN_HISTORY_ID,
                GeoPackageDataType.TEXT, true));
        columns.add(FeatureColumn.createColumn(COLUMN_LAYER_ID,
                GeoPackageDataType.TEXT, true));
        columns.add(FeatureColumn.createColumn(COLUMN_FILE_ID,
                GeoPackageDataType.TEXT));
        columns.add(FeatureColumn.createGeometryColumn(COLUMN_GEOMETRY, GeometryType.GEOMETRY));

        FeatureTable table = new FeatureTable(TABLE_GIS_GEOMETRY_HISTORY, columns);
        geoPackage.createFeatureTable(table);

        contentsDao.create(contents);

        GeometryColumnsDao geometryColumnsDao = geoPackage
                .getGeometryColumnsDao();

        GeometryColumns geometryColumns = new GeometryColumns();
        geometryColumns.setContents(contents);
        geometryColumns.setColumnName(COLUMN_GEOMETRY);
        geometryColumns.setGeometryType(GeometryType.GEOMETRY);
        geometryColumns.setSrs(srs);
        geometryColumns.setZ((byte) 0);
        geometryColumns.setM((byte) 0);
        geometryColumnsDao.create(geometryColumns);
    }

    private static void createGeometryIndexExtension(Context context, GeoPackage geoPackage) {
        List<String> featureTables = geoPackage.getFeatureTables();
        for (String featureTable : featureTables) {

            FeatureDao featureDao = geoPackage.getFeatureDao(featureTable);
            FeatureIndexManager indexer = new FeatureIndexManager(context, geoPackage, featureDao);
            indexer.setIndexLocation(FeatureIndexType.GEOPACKAGE);
            indexer.index();
            indexer.close();
        }

    }

    private static void createGisGeometryPropertyHistoryTable(GeoPackage geoPackage) {
        List<UserCustomColumn> columns = new ArrayList<UserCustomColumn>();

        columns.add(UserCustomColumn.createPrimaryKeyColumn(COLUMN_FID));
        columns.add(UserCustomColumn.createColumn(COLUMN_HISTORY_ID,
                GeoPackageDataType.TEXT, true));
        columns.add(UserCustomColumn.createColumn(COLUMN_GEOMETRY_ID,
                GeoPackageDataType.TEXT));
        columns.add(UserCustomColumn.createColumn(COLUMN_FIELD_ID,
                GeoPackageDataType.TEXT));
        columns.add(UserCustomColumn.createColumn(COLUMN_FIELD_NAME,
                GeoPackageDataType.TEXT));
        columns.add(UserCustomColumn.createColumn(COLUMN_FIELD_VALUE,
                GeoPackageDataType.TEXT));

        UserCustomTable gisGeometryPropertyHistoryTable = new UserCustomTable(TABLE_GIS_GEOMETRY_PROPERTY_HISTORY, columns);
        geoPackage.createUserTable(gisGeometryPropertyHistoryTable);
    }


    private static void createGisLayerStyleTable(GeoPackage geoPackage) {
        List<UserCustomColumn> columns = new ArrayList<UserCustomColumn>();

        columns.add(UserCustomColumn.createPrimaryKeyColumn(COLUMN_FID));
        columns.add(UserCustomColumn.createColumn(COLUMN_PROJECT_ID,
                GeoPackageDataType.TEXT, true));
        columns.add(UserCustomColumn.createColumn(COLUMN_LAYER_ID,
                GeoPackageDataType.TEXT, true));
        columns.add(UserCustomColumn.createColumn(COLUMN_UPDATE_TIME,
                GeoPackageDataType.DATETIME));

        columns.add(UserCustomColumn.createColumn("pointType",
                GeoPackageDataType.INTEGER));
        columns.add(UserCustomColumn.createColumn("pointSize",
                GeoPackageDataType.FLOAT));
        columns.add(UserCustomColumn.createColumn("pointStyle",
                GeoPackageDataType.INTEGER));

        columns.add(UserCustomColumn.createColumn("lineType",
                GeoPackageDataType.INTEGER));
        columns.add(UserCustomColumn.createColumn("lineColor",
                GeoPackageDataType.TEXT));
        columns.add(UserCustomColumn.createColumn("lineOpacity",
                GeoPackageDataType.FLOAT));
        columns.add(UserCustomColumn.createColumn("lineWidth",
                GeoPackageDataType.FLOAT));

        columns.add(UserCustomColumn.createColumn("fillType",
                GeoPackageDataType.INTEGER));
        columns.add(UserCustomColumn.createColumn("fillColor",
                GeoPackageDataType.TEXT));
        columns.add(UserCustomColumn.createColumn("fillOpacity",
                GeoPackageDataType.FLOAT));


        columns.add(UserCustomColumn.createColumn("showAnnotationKey",
                GeoPackageDataType.TEXT));
        columns.add(UserCustomColumn.createColumn("fontSize",
                GeoPackageDataType.FLOAT));
        columns.add(UserCustomColumn.createColumn("fontFamily",
                GeoPackageDataType.TEXT));
        columns.add(UserCustomColumn.createColumn("fontBold",
                GeoPackageDataType.FLOAT));
        columns.add(UserCustomColumn.createColumn("fontItalic",
                GeoPackageDataType.FLOAT));
        columns.add(UserCustomColumn.createColumn("fontFillColor",
                GeoPackageDataType.TEXT));
        columns.add(UserCustomColumn.createColumn("fontStrokeColor",
                GeoPackageDataType.TEXT));
        columns.add(UserCustomColumn.createColumn("fontStrokeWidth",
                GeoPackageDataType.FLOAT));

        columns.add(UserCustomColumn.createColumn("isTemp",
                GeoPackageDataType.INTEGER));

        UserCustomTable gisVersionTable = new UserCustomTable(TABLE_GIS_LAYER_STYLE, columns);
        geoPackage.createUserTable(gisVersionTable);
    }

}